#pragma once 



#include <brpc/closure_guard.h>
#include <csignal>
#include <filesystem>
#include <log.h>
#include <memory>
#include <brpc/server.h>
#include <string>

#include "etcd.hpp"
#include "etcd_path.h"
#include "brpc.h"
#include "Util.hpp"
#include "compile-run.h"
#include "compile.pb.h"

namespace lyt{

    class CompileServiceImpl : public compile::CompileService{
    public:
        using ptr = std::shared_ptr<CompileServiceImpl>;
        CompileServiceImpl(
            const std::string& image_name,
            const std::string& realtionship,
            const std::string& dockerfile_path,
            const std::string& work_path
        )
            :_run(std::make_shared<lyt::Runner>())
            ,_work_path(work_path)
            ,_relationship(realtionship)
        {
            if (false == _run->init(image_name, dockerfile_path)){
                errorlog << "Failed to initializer runner";
                exit(1);
            }
            if (_work_path.back() != '/'){
                _work_path += '/';
            }
            if (lyt::FileUtil::IsExist(_work_path) == false){
                    std::filesystem::create_directories(_work_path);
                }
        }

        virtual void Compile(::google::protobuf::RpcController* controller,
                       const ::compile::CompileRequest* request,
                       ::compile::CompileResponse* response,
                       ::google::protobuf::Closure* done
        ) {
            brpc::ClosureGuard doner(done);
            // 生成uuid作为唯一标识
            std::string id = lyt::Generate_UUID::generate_uuid();
            auto handler = [&](int status,const std::string& output, const std::string& err_msg = ""){
                response->set_status(status);
                response->set_result(output);
                response->set_err_msg(err_msg);
                delete_file(id);
                return;
            };
            // 获取到代码和输入
            std::string code = request->code();
            std::string input = request->input();
            
            // 定义各种需要的名称对象
            // std::string src_path ="/home/lyt/OnlineJudge/build/src/compile_server/";
            std::string src_file =  id + ".cc";
            std::string exec_file = id;
            std::string error_file =  id + ".error";
            std::string output_file =   id + ".output";
            std::string input_file = id + ".input";
            std::string quit_code_file = id + ".quit";
            // 写入代码和测试用例
            if (false == lyt::FileUtil::WriteText(  _work_path + src_file, code)){
                debuglog << "写入代码失败";
                return handler(1, "","内部错误");
            }
            if (false == lyt::FileUtil::WriteText(  _work_path + input_file, input)){
                debuglog << "写入输入失败";
                return handler(1, "","内部错误");
            }
            // 编译代码
            if (false == _run->compile(_relationship, src_file, exec_file, error_file) // 编译失败
                || lyt::FileUtil::IsExist( exec_file) // 没有可执行程序生成
            ){
                // 编译错误 , 或者没有生成可执行程序
                warninglog << "编译错误 or 编译失败";
                return handler(2, "",lyt::FileUtil::ReadText(_work_path + error_file));
            }
            // 运行代码
            _run->run(_relationship, exec_file, input_file, output_file, error_file,_work_path +quit_code_file);
            std::string output = lyt::FileUtil::ReadText(_work_path + output_file);
            std::string error = lyt::FileUtil::ReadText(_work_path + error_file);
            int res = std::stoi(lyt::FileUtil::ReadText(_work_path + quit_code_file));
            infolog << "code : " << res;
            // 139 = 128 + 11 (11就是段错误、栈溢出退出码)
            // 137 = 128 + 9 (超时)
            // 132 = 128 + 4（浮点数错误）
            // 134 = 128 + 6（abort）
            switch (res) {
                case 0:{
                    // 正常
                    return handler(0, output,error);
                }
                case 11 + 128:{
                    // 断错误
                    return handler(3, output,error);
                }
                case 4 + 128:{
                    // 浮点数异常
                    return handler(4, output,error);
                }
                case 9 + 128:{
                    // 超时
                    return handler(5, output,"Timeout");
                }
                case 6 + 128:{
                    // abort
                    return handler(6, output,error);
                }
                default:{
                    return handler(1, output,error);
                }
            }

        }

    private:
        void delete_file(const std::string& id) {
            std::string src_file = _work_path + id + ".cc";
            std::string exec_file = _work_path + id;
            std::string error_file =  _work_path + id + ".error";
            std::string output_file =   _work_path + id + ".output";
            std::string input_file = _work_path + id + ".input";
            std::string quit_code_file = _work_path + id + ".quit";
            unlink(src_file.data());
            unlink(exec_file.data());
            unlink(error_file.data());
            unlink(output_file.data());
            unlink(input_file.data());
            unlink(quit_code_file.data());
        }

    private:
        lyt::Runner::ptr _run;
        std::string _work_path;
        std::string _relationship;
    };

    class CompileServer{
    private:
        std::shared_ptr<brpc::Server> _server;
        lyt::Register::ptr _rigister;
        CompileServiceImpl::ptr _compile;
    public:
        using ptr = std::shared_ptr<CompileServer>;
        CompileServer(const CompileServiceImpl::ptr compile
                    ,const std::shared_ptr<brpc::Server> svr
                    ,const lyt::Register::ptr regis)
                    :_server(svr),_rigister(regis),_compile(compile){}
        void start(){
            _server->RunUntilAskedToQuit();
        }
    };
    class CompileServerBuild{
    private:
        std::shared_ptr<brpc::Server> _server;
        CompileServiceImpl::ptr _compile;
        lyt::Register::ptr _register;

    public:
        using ptr = std::shared_ptr<CompileServerBuild>;
        CompileServerBuild(){}
        
        CompileServerBuild& make_compile_service(
            const std::string& image_name,
            const std::string& realtionship,
            const std::string& dockerfile_path,
            const std::string& work_path
        ){
            _compile = std::make_shared<CompileServiceImpl>(image_name,realtionship,dockerfile_path,work_path);
            return *this;
        }
        CompileServerBuild& make_brpc_server(const std::size_t& port){
            if(!_compile){
                errorlog << "UserService uninitizalier";
                abort();
            }
            _server = std::make_shared<brpc::Server>();
            if(_server->AddService(&(*_compile),brpc::SERVER_DOESNT_OWN_SERVICE) != 0){
                errorlog << "Failed to add service: UserService";
                abort();
            }
            brpc::ServerOptions options;
            options.idle_timeout_sec = -1;
            if(_server->Start(port,&options) != 0){
                errorlog << "Failed to start service: UserService";
                abort();
            }
            return *this;
        }
        CompileServerBuild& make_register(const std::string& host,const std::string& value) {

            if(!_server){
                errorlog << "Can't register to etcd,because UserService not start";
                abort();
            }
            _register = std::make_shared<lyt::Register>(host);
            if(!_register->registory(lyt::compile_path + value, value)){
                errorlog << "Fail register UserService to etcd";
                abort();
            }
            return *this;    
        }
        CompileServer::ptr build() {
            if (!_server) {
                errorlog << "failed to brpc uninitizalier";
                abort();
            }
            if (!_compile) {
                errorlog << "failed to compile uninitizalier";
                abort();
            }
            if (!_register) {
                errorlog << "failed to register uninitizalier";
                abort();
            }
            return std::shared_ptr<CompileServer>(new CompileServer(_compile,_server,_register));
        }
    };
}